<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<!--
Author: attila@google.com (Attila Bodis)
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Closure Unit Tests - goog.ui.MenuItem</title>
  <script src="../base.js"></script>
  <script>
    goog.require('goog.array');
    goog.require('goog.dom');
    goog.require('goog.dom.NodeType');
    goog.require('goog.dom.classlist');
    goog.require('goog.events.KeyCodes');
    goog.require('goog.testing.events');
    goog.require('goog.testing.jsunit');
    goog.require('goog.testing.recordFunction');
    goog.require('goog.ui.Component.State');
    goog.require('goog.ui.MenuItem');
    goog.require('goog.ui.MenuItemRenderer');
  </script>
</head>
<body>
  <div id="sandbox"></div>
  <div id="parentComponent"></div>
  <script>
    var sandbox = goog.dom.getElement('sandbox');
    var item;

    function setUp() {
      item = new goog.ui.MenuItem('Item');
    }

    function tearDown() {
      item.dispose();
      goog.dom.removeChildren(sandbox);
    }

    function testMenuItem() {
      assertNotNull('Instance must not be null', item);
      assertEquals('Renderer must default to MenuItemRenderer singleton',
          goog.ui.MenuItemRenderer.getInstance(), item.getRenderer());
      assertEquals('Content must have expected value', 'Item',
          item.getContent());
      assertEquals('Caption must default to the content', item.getContent(),
          item.getCaption());
      assertEquals('Value must default to the caption', item.getCaption(),
          item.getValue());
    }

    function testMenuItemConstructor() {
      var model = 'Hello';
      var fakeDom = {};
      var fakeRenderer = {};

      var menuItem = new goog.ui.MenuItem('Item', model, fakeDom, fakeRenderer);
      assertEquals('Content must have expected value', 'Item',
          menuItem.getContent());
      assertEquals('Caption must default to the content', menuItem.getContent(),
          menuItem.getCaption());
      assertEquals('Model must be set', model, menuItem.getModel());
      assertNotEquals('Value must not equal the caption', menuItem.getCaption(),
          menuItem.getValue());
      assertEquals('Value must equal the model', model, menuItem.getValue());
      assertEquals('DomHelper must be set', fakeDom, menuItem.getDomHelper());
      assertEquals('Renderer must be set', fakeRenderer,
          menuItem.getRenderer());
    }

    function testGetValue() {
      assertUndefined('Model must be undefined by default', item.getModel());
      assertEquals('Without a model, value must default to the caption',
          item.getCaption(), item.getValue());
      item.setModel('Foo');
      assertEquals('With a model, value must default to the model',
          item.getModel(), item.getValue());
    }

    function testSetValue() {
      assertUndefined('Model must be undefined by default', item.getModel());
      assertEquals('Without a model, value must default to the caption',
          item.getCaption(), item.getValue());
      item.setValue(17);
      assertEquals('Value must be set', 17, item.getValue());
      assertEquals('Value and model must be the same', item.getValue(),
          item.getModel());
    }

    function testGetSetContent() {
      assertEquals('Content must have expected value', 'Item',
          item.getContent());
      item.setContent(goog.dom.createDom('div', 'foo', 'Foo'));
      assertEquals('Content must be an element', goog.dom.NodeType.ELEMENT,
          item.getContent().nodeType);
      assertHTMLEquals('Content must be the expected element',
          '<div class="foo">Foo</div>',
          goog.dom.getOuterHtml(item.getContent()));
    }

    function testGetSetCaption() {
      assertEquals('Caption must have expected value', 'Item',
          item.getCaption());
      item.setCaption('Hello, world!');
      assertTrue('Caption must be a string', goog.isString(item.getCaption()));
      assertEquals('Caption must have expected value', 'Hello, world!',
          item.getCaption());
      item.setContent(goog.dom.createDom('div', 'foo', 'Foo'));
      assertTrue('Caption must be a string', goog.isString(item.getCaption()));
      assertEquals('Caption must have expected value', 'Foo',
          item.getCaption());
    }

    function testGetSetContentAfterCreateDom() {
      item.createDom();
      assertEquals('Content must have expected value', 'Item',
          item.getContent());
      item.setContent(goog.dom.createDom('div', 'foo', 'Foo'));
      assertEquals('Content must be an element', goog.dom.NodeType.ELEMENT,
          item.getContent().nodeType);
      assertHTMLEquals('Content must be the expected element',
          '<div class="foo">Foo</div>',
          goog.dom.getOuterHtml(item.getContent()));
    }

    function testGetSetCaptionAfterCreateDom() {
      item.createDom();
      assertEquals('Caption must have expected value', 'Item',
          item.getCaption());
      item.setCaption('Hello, world!');
      assertTrue('Caption must be a string', goog.isString(item.getCaption()));
      assertEquals('Caption must have expected value', 'Hello, world!',
          item.getCaption());
      item.setContent(goog.dom.createDom('div', 'foo', 'Foo'));
      assertTrue('Caption must be a string', goog.isString(item.getCaption()));
      assertEquals('Caption must have expected value', 'Foo',
          item.getCaption());

      var arrayContent = goog.array.clone(goog.dom.htmlToDocumentFragment(
          ' <b> \xa0foo</b><i>  bar</i> ').childNodes);
      item.setContent(arrayContent);
      assertEquals('whitespaces must be normalized in the caption',
          '\xa0foo bar', item.getCaption());
    }

    function testSetSelectable() {
      assertFalse('Item must not be selectable by default',
          item.isSupportedState(goog.ui.Component.State.SELECTED));
      item.setSelectable(true);
      assertTrue('Item must be selectable',
          item.isSupportedState(goog.ui.Component.State.SELECTED));
      item.setSelected(true);
      assertTrue('Item must be selected', item.isSelected());
      assertFalse('Item must not be checked', item.isChecked());
      item.setSelectable(false);
      assertFalse('Item must not no longer be selectable',
          item.isSupportedState(goog.ui.Component.State.SELECTED));
      assertFalse('Item must no longer be selected', item.isSelected());
      assertFalse('Item must not be checked', item.isChecked());
    }

    function testSetCheckable() {
      assertFalse('Item must not be checkable by default',
          item.isSupportedState(goog.ui.Component.State.CHECKED));
      item.setCheckable(true);
      assertTrue('Item must be checkable',
          item.isSupportedState(goog.ui.Component.State.CHECKED));
      item.setChecked(true);
      assertTrue('Item must be checked', item.isChecked());
      assertFalse('Item must not be selected', item.isSelected());
      item.setCheckable(false);
      assertFalse('Item must not no longer be checkable',
          item.isSupportedState(goog.ui.Component.State.CHECKED));
      assertFalse('Item must no longer be checked', item.isChecked());
      assertFalse('Item must not be selected', item.isSelected());
    }

    function testSetSelectableBeforeCreateDom() {
      item.setSelectable(true);
      item.createDom();
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      item.setSelectable(false);
      assertFalse('Item must no longer have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
    }

    function testSetCheckableBeforeCreateDom() {
      item.setCheckable(true);
      item.createDom();
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      item.setCheckable(false);
      assertFalse('Item must no longer have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
    }

    function testSetSelectableAfterCreateDom() {
      item.createDom();
      item.setSelectable(true);
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      item.setSelectable(false);
      assertFalse('Item must no longer have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
    }

    function testSetCheckableAfterCreateDom() {
      item.createDom();
      item.setCheckable(true);
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      item.setCheckable(false);
      assertFalse('Item must no longer have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
    }

    function testSelectableBehavior() {
      item.setSelectable(true);
      item.render(sandbox);
      assertFalse('Item must not be selected by default', item.isSelected());
      item.performActionInternal();
      assertTrue('Item must be selected', item.isSelected());
      item.performActionInternal();
      assertTrue('Item must still be selected', item.isSelected());
    }

    function testCheckableBehavior() {
      item.setCheckable(true);
      item.render(sandbox);
      assertFalse('Item must not be checked by default', item.isChecked());
      item.performActionInternal();
      assertTrue('Item must be checked', item.isChecked());
      item.performActionInternal();
      assertFalse('Item must no longer be checked', item.isChecked());
    }

    function testGetSetContentForItemWithCheckBox() {
      item.setSelectable(true);
      item.createDom();

      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      assertEquals('getContent() must not return the checkbox structure',
          'Item', item.getContent());

      item.setContent('Hello');
      assertEquals('getContent() must not return the checkbox structure',
          'Hello', item.getContent());
      assertTrue('Item must still have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));

      item.setContent(goog.dom.createDom('span', 'foo', 'Foo'));
      assertEquals('getContent() must return element',
          goog.dom.NodeType.ELEMENT, item.getContent().nodeType);
      assertTrue('Item must still have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));

      item.setContent(null);
      assertNull('getContent() must return null', item.getContent());
      assertTrue('Item must still have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
    }

    function testGetSetCaptionForItemWithCheckBox() {
      item.setCheckable(true);
      item.createDom();

      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      assertEquals('getCaption() must not return the checkbox structure',
          'Item', item.getCaption());

      item.setCaption('Hello');
      assertEquals('getCaption() must not return the checkbox structure',
          'Hello', item.getCaption());
      assertTrue('Item must still have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));

      item.setContent(goog.dom.createDom('span', 'foo', 'Foo'));
      assertEquals('getCaption() must return text content', 'Foo',
          item.getCaption());
      assertTrue('Item must still have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));

      item.setCaption('');
      assertEquals('getCaption() must return empty string', '',
          item.getCaption());
      assertTrue('Item must still have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
    }

    function testGetSetCaptionForItemWithAccelerators() {
      var contentArr = [];
      contentArr.push(goog.dom.createDom('span',
          goog.getCssName('goog-menuitem-accel'), 'Ctrl+1'));
      contentArr.push(goog.dom.createTextNode('Hello'));
      item.setCaption(contentArr);
      assertEquals('getCaption() must not return the accelerator', 'Hello',
          item.getCaption());

      item.setCaption('');
      assertEquals('getCaption() must return empty string', '',
          item.getCaption());
    }

    function testGetSetCaptionForItemWithMnemonics() {
      var contentArr = [];
      contentArr.push(goog.dom.createDom('span',
          goog.getCssName('goog-menuitem-mnemonic-hint'), 'H'));
      contentArr.push(goog.dom.createTextNode('ello'));
      item.setCaption(contentArr);
      assertEquals('getCaption() must not return hint markup', 'Hello',
          item.getCaption());

      contentArr = [];
      contentArr.push(goog.dom.createTextNode('Hello'));
      contentArr.push(goog.dom.createDom('span',
          goog.getCssName('goog-menuitem-mnemonic-separator'), '(',
          goog.dom.createDom('span',
              goog.getCssName('goog-menuitem-mnemonic-hint'), 'J'), ')'));
      item.setCaption(contentArr);
      assertEquals('getCaption() must not return the paranethetical mnemonic',
          'Hello', item.getCaption());

      item.setCaption('');
      assertEquals('getCaption() must return the empty string', '',
      item.getCaption());
    }

    function testHandleKeyEventInternalWithMnemonic() {
      item.performActionInternal =
          goog.testing.recordFunction(item.performActionInternal);
      item.setMnemonic(goog.events.KeyCodes.F);
      item.handleKeyEventInternal({'keyCode': goog.events.KeyCodes.F});
      assertEquals('performActionInternal must be called', 1,
          item.performActionInternal.getCallCount());
    }

    function testHandleKeyEventInternalWithoutMnemonic() {
      item.performActionInternal = goog.testing.recordFunction(
          item.performActionInternal);
      item.handleKeyEventInternal({'keyCode': goog.events.KeyCodes.F});
      assertEquals('performActionInternal must not be called without a' +
          ' mnemonic', 0, item.performActionInternal.getCallCount());
    }

    function testRender() {
      item.render(sandbox);
      var contentElement = item.getContentElement();
      assertNotNull('Content element must exist', contentElement);
      assertTrue('Content element must have expected class name',
          goog.dom.classlist.contains(contentElement,
              item.getRenderer().getStructuralCssClass() + '-content'));
      assertHTMLEquals('Content element must have expected structure',
          'Item', contentElement.innerHTML);
    }

    function testRenderSelectableItem() {
      item.setSelectable(true);
      item.render(sandbox);
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      assertEquals('getCaption() return expected value', 'Item',
          item.getCaption());
    }

    function testRenderSelectedItem() {
      item.setSelectable(true);
      item.setSelected(true);
      item.render(sandbox);
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      assertTrue('Item must have selected style',
          goog.dom.classlist.contains(item.getElement(),
              item.getRenderer().getClassForState(
                  goog.ui.Component.State.SELECTED)));
    }

    function testRenderCheckableItem() {
      item.setCheckable(true);
      item.render(sandbox);
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      assertEquals('getCaption() return expected value', 'Item',
          item.getCaption());
    }

    function testRenderCheckedItem() {
      item.setCheckable(true);
      item.setChecked(true);
      item.render(sandbox);
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      assertTrue('Item must have checked style',
          goog.dom.classlist.contains(item.getElement(),
              item.getRenderer().getClassForState(
                  goog.ui.Component.State.CHECKED)));
    }

    function testDecorate() {
      sandbox.innerHTML = '<div id="foo">Foo</div>';
      var foo = goog.dom.getElement('foo');
      item.decorate(foo);
      assertEquals('Decorated element must be as expected', foo,
          item.getElement());
      assertTrue('Decorated element must have expected class name',
          goog.dom.classlist.contains(item.getElement(),
              item.getRenderer().getCssClass()));
      assertEquals('Content element must be the decorated element\'s child',
          item.getContentElement(), item.getElement().firstChild);
      assertHTMLEquals('Content must have expected structure', 'Foo',
          item.getContentElement().innerHTML);
    }

    function testDecorateCheckableItem() {
      sandbox.innerHTML = '<div id="foo" class="goog-option">Foo</div>';
      var foo = goog.dom.getElement('foo');
      item.decorate(foo);
      assertEquals('Decorated element must be as expected', foo,
          item.getElement());
      assertTrue('Decorated element must have expected class name',
          goog.dom.classlist.contains(item.getElement(),
              item.getRenderer().getCssClass()));
      assertEquals('Content element must be the decorated element\'s child',
          item.getContentElement(), item.getElement().firstChild);
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      assertFalse('Item must not be checked', item.isChecked());
    }

    function testDecorateCheckedItem() {
      sandbox.innerHTML =
          '<div id="foo" class="goog-option goog-option-selected">Foo</div>';
      var foo = goog.dom.getElement('foo');
      item.decorate(foo);
      assertEquals('Decorated element must be as expected', foo,
          item.getElement());
      assertSameElements('Decorated element must have expected class names',
          ['goog-menuitem', 'goog-option', 'goog-option-selected'],
          goog.dom.classlist.get(item.getElement()));
      assertEquals('Content element must be the decorated element\'s child',
          item.getContentElement(), item.getElement().firstChild);
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      assertTrue('Item must be checked', item.isChecked());
    }

    function testDecorateTemplate() {
      sandbox.innerHTML = '<div id="foo" class="goog-menuitem">' +
          '<div class="goog-menuitem-content">Foo</div></div>';
      var foo = goog.dom.getElement('foo');
      item.decorate(foo);
      assertEquals('Decorated element must be as expected', foo,
          item.getElement());
      assertTrue('Decorated element must have expected class name',
          goog.dom.classlist.contains(item.getElement(),
              item.getRenderer().getCssClass()));
      assertEquals('Content element must be the decorated element\'s child',
          item.getContentElement(), item.getElement().firstChild);
      assertHTMLEquals('Content must have expected structure', 'Foo',
          item.getContentElement().innerHTML);
    }

    function testDecorateCheckableItemTemplate() {
      sandbox.innerHTML = '<div id="foo" class="goog-menuitem goog-option">' +
          '<div class="goog-menuitem-content">' +
          '<div class="goog-menuitem-checkbox"></div>' +
          'Foo</div></div>';
      var foo = goog.dom.getElement('foo');
      item.decorate(foo);
      assertEquals('Decorated element must be as expected', foo,
          item.getElement());
      assertTrue('Decorated element must have expected class name',
          goog.dom.classlist.contains(item.getElement(),
              item.getRenderer().getCssClass()));
      assertEquals('Content element must be the decorated element\'s child',
          item.getContentElement(), item.getElement().firstChild);
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      assertEquals('Item must have exactly one checkbox structure', 1,
          goog.dom.getElementsByTagNameAndClass('div', 'goog-menuitem-checkbox',
              item.getElement()).length);
      assertFalse('Item must not be checked', item.isChecked());
    }

    function testDecorateCheckedItemTemplate() {
      sandbox.innerHTML = '<div id="foo" ' +
          'class="goog-menuitem goog-option goog-option-selected">' +
          '<div class="goog-menuitem-content">' +
          '<div class="goog-menuitem-checkbox"></div>' +
          'Foo</div></div>';
      var foo = goog.dom.getElement('foo');
      item.decorate(foo);
      assertEquals('Decorated element must be as expected', foo,
          item.getElement());
      assertSameElements('Decorated element must have expected class names',
          ['goog-menuitem', 'goog-option', 'goog-option-selected'],
          goog.dom.classlist.get(item.getElement()));
      assertEquals('Content element must be the decorated element\'s child',
          item.getContentElement(), item.getElement().firstChild);
      assertTrue('Item must have checkbox structure',
          item.getRenderer().hasCheckBoxStructure(item.getElement()));
      assertEquals('Item must have exactly one checkbox structure', 1,
          goog.dom.getElementsByTagNameAndClass('div', 'goog-menuitem-checkbox',
              item.getElement()).length);
      assertTrue('Item must be checked', item.isChecked());
    }

    /** @bug 1463524 */
    function testHandleMouseUp() {
      var COORDS_1 = new goog.math.Coordinate(1, 1);
      var COORDS_2 = new goog.math.Coordinate(2, 2);
      item.setActive(true);
      // Override performActionInternal() for testing purposes.
      var actionPerformed;
      item.performActionInternal = function() {
        actionPerformed = true;
        return true;
      };
      item.render(sandbox);

      // Scenario 1: item has no parent.
      actionPerformed = false;
      item.setActive(true);
      goog.testing.events.fireMouseUpEvent(item.getElement());
      assertTrue('Action should be performed on mouseup', actionPerformed);

      // Scenario 2: item has a parent.
      actionPerformed = false;
      item.setActive(true);
      var parent = new goog.ui.Component();
      var parentElem = goog.dom.getElement('parentComponent');
      parent.render(parentElem);
      parent.addChild(item);
      parent.openingCoords = COORDS_1;
      goog.testing.events.fireMouseUpEvent(
          item.getElement(), undefined, COORDS_2);
      assertTrue('Action should be performed on mouseup', actionPerformed);

      // Scenario 3: item has a parent which was opened during mousedown, and
      // item, and now the mouseup fires at the same coords.
      actionPerformed = false;
      item.setActive(true);
      parent.openingCoords = COORDS_2;
      goog.testing.events.fireMouseUpEvent(
          item.getElement(), undefined, COORDS_2);
      assertFalse('Action should not be performed on mouseup', actionPerformed);
    }

  </script>
</body>
</html>
